﻿#region File Description

//-----------------------------------------------------------------------------
// MovieController.cs
//
// Author: ITU Team3 (2012)
//-----------------------------------------------------------------------------

#endregion File Description

#region Using Statements

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using rentit03_client.Models;
using rentit03_client.rentit03;

#endregion

namespace rentit03_client.Controllers
{
    public class MovieController : RentitController
    {
        readonly ServiceClient _service = new ServiceClient();

        /// <summary>
        /// Retrieve a specific movie object
        /// </summary>
        /// <param name="id">The id of the movie object to retrieve</param>
        /// <returns>A movie object</returns>
        private MovieObj ReadMovie(int id)
        {
            var r = _service.ReadMedia(id, Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);
            if (ro.Success)
            {
                var movie = MagicJSON.Deserialize<MovieObj>(ro.Value);

                if (!String.IsNullOrWhiteSpace(movie.Released))
                {
                    var date = DateTime.Parse(movie.Released);
                    movie.Released = date.ToString("MMMM yyyy");
                }

                movie.RatingPercent = (int)(movie.Rating * 10);

                if (String.IsNullOrWhiteSpace(movie.CoverUrl))
                    movie.CoverUrl = Url.Content("~/Content/img/no_poster.png");
                if (movie.Rating > 0)
                    movie.Rating = (float)Double.Parse(movie.Rating.ToString("#.#"));

                return movie;
            }

            throw new NotImplementedException();
        }

        /// <summary>
        /// Creates the index page of the website and displays a movie id if id is inserted
        /// </summary>
        /// <param name="id">The movie to view</param>
        /// <returns>The <seealso cref="ActionResult"/> containing index action</returns>
        public ActionResult Index(int id = -1)
        {
            if (id > 0)
            {
                ViewBag.Movie = ReadMovie(id);
                return View();
            }

            ViewBag.Media = Search();
            return View("Overview");
        }

        /// <summary>
        /// Creates the overview function on the website to view movie in "pages"
        /// </summary>
        /// <param name="page">The page number to view</param>
        /// <param name="count">The number of movies displayed on each page</param>
        /// <returns>The <seealso cref="ActionResult"/> containing overview action</returns>
        public ActionResult Overview(int page = 1, int count = 12)
        {
            var search = new OverviewMediaParemObj { Page = page - 1, Count = count };

            var max = OverviewCount(search);
            ViewBag.Pager = new Pager { Current = page, Count = count, PageCount = (int)Math.Ceiling((double)(max) / count), Action = "Overview", Controller = "Movie" };

            ViewBag.Title = "Movies";
            ViewBag.Media = Search(search);
            return View("Overview");
        }

        /// <summary>
        /// Counts the number of medias with the given search parameters
        /// </summary>
        /// <param name="searchObj">The media search parameter to count for</param>
        /// <returns>The number of media objects</returns>
        private static int OverviewCount(OverviewMediaParemObj searchObj)
        {
            var r = new ServiceClient().OverviewMediaCount(searchObj.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            return (ro.Success) ? Convert.ToInt32(ro.Value) : 0;
        }

        /// <summary>
        /// Returns the user to index page if it is not allowed to create a media
        /// </summary>
        /// <returns>The <seealso cref="ActionResult"/> containing the create action</returns>
        public ActionResult Create()
        {
            if (!User.Permissions.Contains("CreateMedia"))
            {
                return RedirectToAction("Index");
            }

            ViewBag.Title = "Create movie";
            return View("Form", new MovieViewModel());
        }

        /// <summary>
        /// Edits or creates a media
        /// </summary>
        /// <param name="model">The model containing the media changes or the new media</param>
        /// <returns>The <seealso cref="ActionsResult"/> containing the save action</returns>
        [HttpPost]
        public ActionResult Save(MovieViewModel model)
        {
            var creating = model.Id <= 0;

            if (!ModelState.IsValid)
            {
                return View("Form", model);
            }

            var genres = (model.Genres != null)
                ? model.Genres.Select(genre => new GenreObj { MediaType = "Movie", Name = genre }).ToList()
                : new List<GenreObj>();
            if (model.AddtionalGenres != null)
                genres.AddRange(model.AddtionalGenres.Trim().Split(',').Select(genre => new GenreObj { MediaType = "Movie", Name = genre.Trim() }));

            var languages = model.Languages ?? new List<string>();
            if (model.AddtionalLanguages != null)
                languages.AddRange(model.AddtionalLanguages.Trim().Split(',').Select(language => language.Trim()));

            var movie = new MovieObj
            {
                Title = model.Title,
                Type = "Movie",
                Year = model.Year,
                Description = model.Plot,
                CoverUrl = model.Cover,
                Genres = genres,
                ImdbId = model.Imdb,
                Runtime = model.Hours * 60 + model.Mins,
                Rated = model.Rating,
                Languages = languages,
                Released = model.Released,
                // TODO: Hardcoded to simplify client
                Versions = new List<VersionObj>
                {
                    new VersionObj {Format = ".mpeg", Price = 99, Resolution = "HD"},
                    new VersionObj {Format = ".mpeg", Price = 79, Resolution = "SD"},
                    new VersionObj
                        {Format = ".mpeg", Price = 49, RentDays = 3, Resolution = "HD"},
                    new VersionObj
                        {Format = ".mpeg", Price = 39, RentDays = 3, Resolution = "SD"},
                }
            };

            // Create user at service
            var r = creating ? _service.CreateMedia(movie.ToJSON(), Sid) : _service.EditMedia(model.Id, movie.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            // Check if everything went well
            if (!ro.Success)
            {
                switch (ro.Status)
                {
                    case 304:
                        ModelState.AddModelError("Title", ro.Message);
                        break;
                    default:
                        // TODO: Change to ro.Value
                        ModelState.AddModelError("Title", "Error: " + r);
                        break;
                }

                return View("Form", model);
            }

            return RedirectToAction("Index", new { id = ro.Value });
        }

        /// <summary>
        /// Deletes a specific media
        /// </summary>
        /// <param name="id">The id of the media to delete</param>
        /// <param name="delete">True if the media should be deleted</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the delete action</returns>
        public ActionResult Delete(int id, bool delete = false)
        {
            if (delete)
            {
                var r = new ServiceClient().DeleteMedia(id, Sid);
                var ro = MagicJSON.Deserialize<ReturnObj>(r);

                if (ro.Success)
                    return RedirectToAction("Index");

                ViewBag.message = ro.Message;
            }

            ViewBag.movieTitle = ReadMovie(id).Title;

            ViewBag.id = id;
            return View();
        }

        /// <summary>
        /// Searches for a media
        /// </summary>
        /// <param name="model">The search parameter object containing the parameters to search for</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the search action</returns>
        [HttpGet]
        public ActionResult Search(SearchViewModel model)
        {
            var searchObj = new OverviewMediaParemObj();

            if (!String.IsNullOrWhiteSpace(model.Title))
                searchObj.Title = model.Title;

            ViewBag.Media = Search(searchObj);
            ViewBag.Title = "Search Results";

            return View("Overview");
        }

        /// <summary>
        /// Searches for a movie
        /// </summary>
        /// <param name="searchObj">The movie search parameter object to search for</param>
        /// <returns>A list of movie objects</returns>
        private static List<MovieObj> Search(OverviewMediaParemObj searchObj = default(OverviewMediaParemObj))
        {
            var r = new ServiceClient().OverviewMedia(searchObj.ToJSON(), Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            return (ro.Success) ? MagicJSON.Deserialize<List<MovieObj>>(ro.Value) : new List<MovieObj>();
        }

        /// <summary>
        /// Retrieves the top rated media
        /// </summary>
        /// <param name="page">The page to view</param>
        /// <param name="count">The number of medias per page</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the toprated action</returns>
        public ActionResult TopRated(int page = 1, int count = 10)
        {
            ViewBag.Media = GetTopRated(page, count);
            var pageCount = (int)Math.Ceiling((double)(OverviewCount(new OverviewMediaParemObj())) / count);
            ViewBag.Pager = new Pager { Current = page, Count = count, PageCount = pageCount, Action = "TopRated", Controller = "Movie" };
            ViewBag.Title = "Top Rated Movies";
            return View();
        }

        /// <summary>
        /// Retrieves the top rated movies
        /// </summary>
        /// <param name="page">The page number to view</param>
        /// <param name="count">The number of movies per page</param>
        /// <returns>A list of movie objects</returns>
        public List<MovieObj> GetTopRated(int page = 1, int count = 12)
        {
            var service = new ServiceClient();
            var r = service.TopRatedMovies(page - 1, count, Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            if (ro.Success)
            {
                return MagicJSON.Deserialize<List<MovieObj>>(ro.Value);
            }

            throw new NotImplementedException();
        }

        /// <summary>
        /// Retrieves a random movie
        /// </summary>
        /// <param name="count">The number of random movies</param>
        /// <returns>A list of movie objects</returns>
        public List<MovieObj> GetRandom(int count = 12)
        {
            var service = new ServiceClient();
            var r = service.RandomMovies(count, Sid);
            var ro = MagicJSON.Deserialize<ReturnObj>(r);

            if (ro.Success)
            {
                return MagicJSON.Deserialize<List<MovieObj>>(ro.Value);
            }

            throw new NotImplementedException();
        }

        /// <summary>
        /// Edits a movie
        /// </summary>
        /// <param name="id">The id of the movie to edit</param>
        /// <returns>The <seealso cref="ActionResult"/> containing the edit action</returns>
        public ActionResult Edit(int id)
        {
            if (String.IsNullOrWhiteSpace(Sid))
                return RedirectToAction("Login", "User");
            if (!User.Permissions.Contains("EditMedia"))
                return RedirectToAction("Index", "Movie", new { id });

            var movie = ReadMovie(id);

            var model = new MovieViewModel
            {
                Id = movie.Id,
                Title = movie.Title,
                Year = movie.Year,
                Plot = movie.Description,
                Cover = movie.CoverUrl,
                Genres = movie.Genres.Select(g => g.Name).ToList(),
                Imdb = movie.ImdbId,
                Hours = movie.Runtime / 60,
                Mins = movie.Runtime % 60,
                Rating = movie.Rated,
                Languages = movie.Languages,
                Released = movie.Released
            };

            ViewBag.Title = "Edit movie";

            return View("Form", model);
        }
    }

    public class Pager
    {
        public int Count { get; set; }

        public int Current { get; set; }

        public int PageCount { get; set; }

        public string Action { get; set; }

        public string Controller { get; set; }
    }

    public class SearchViewModel
    {
        public string Title { get; set; }

        public string Year { get; set; }
    }

    public class MovieViewModel
    {
        public int Id { get; set; }

        [Required]
        [DataType(DataType.Text)]
        public string Title { get; set; }

        [Required]
        [Range(1888, 2020)]
        public int Year { get; set; }

        [DataType(DataType.MultilineText)]
        public string Plot { get; set; }

        [DataType(DataType.Url)]
        [Display(Name = "Cover URL")]
        public string Cover { get; set; }

        public List<string> Genres { get; set; }

        [DataType(DataType.Text)]
        public string AddtionalGenres { get; set; }

        public HashSet<string> ListGenres
        {
            get
            {
                var r = new ServiceClient().OverviewGenres(String.Empty);
                var ro = MagicJSON.Deserialize<ReturnObj>(r);
                return ro.Success ? new HashSet<string>(MagicJSON.Deserialize<List<string>>(ro.Value)) : new HashSet<string>();
            }
        }

        [DataType(DataType.Text)]
        [Display(Name = "IMDb Id")]
        public string Imdb { get; set; }

        [Range(0, 100)]
        [Display(Name = "Runtime")]
        public int Hours { get; set; }

        [Range(0, 59)]
        public int Mins { get; set; }

        [DataType(DataType.Text)]
        public string Rating { get; set; }

        public List<string> Languages { get; set; }

        [DataType(DataType.Text)]
        public string AddtionalLanguages { get; set; }

        public HashSet<string> ListLanguages
        {
            get
            {
                var r = new ServiceClient().OverviewLanguages(String.Empty);
                var ro = MagicJSON.Deserialize<ReturnObj>(r);
                return ro.Success ? new HashSet<string>(MagicJSON.Deserialize<List<string>>(ro.Value)) : new HashSet<string>();
            }
        }

        [DataType(DataType.Date)]
        public string Released { get; set; }
    }
}